﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ПрограммныйИнтерфейс

#Область ДляВызоваИзДругихПодсистем

// СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки

// Возвращает сведения о внешней обработке.
//
// Возвращаемое значение:
//   см. ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке
//
Функция СведенияОВнешнейОбработке() Экспорт
	Перем ПараметрыРегистрации;
	
	Если ПодсистемаСуществует("СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки") Тогда
		МодульДополнительныеОтчетыИОбработки = ОбщийМодуль("ДополнительныеОтчетыИОбработки");
		МодульДополнительныеОтчетыИОбработкиКлиентСервер = ОбщийМодуль("ДополнительныеОтчетыИОбработкиКлиентСервер");
		
		ПараметрыРегистрации = МодульДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.1.3.1");// см. ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке 
		
		ПараметрыРегистрации.Вид = МодульДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительнаяОбработка();
		ПараметрыРегистрации.Версия = "2.2.1";
		ПараметрыРегистрации.БезопасныйРежим = Ложь;
		
		НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
		НоваяКоманда.Представление = НСтр("ru = 'Групповое изменение реквизитов'");
		НоваяКоманда.Идентификатор = "ОткрытьГлобально";
		НоваяКоманда.Использование = МодульДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
		НоваяКоманда.ПоказыватьОповещение = Ложь;
	КонецЕсли;
	
	Возврат ПараметрыРегистрации;
	
КонецФункции

// Конец СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Для внутреннего использования.
Функция ТекстЗапроса(ТипыИзменяемыхОбъектов, ОграничитьВыборку = Ложь) Экспорт
	
	ОбъектыМетаданных = Новый Массив;
	Для Каждого ИмяОбъекта Из СтрРазделить(ТипыИзменяемыхОбъектов, ",", Ложь) Цикл
		ОбъектыМетаданных.Добавить(Метаданные.НайтиПоПолномуИмени(ИмяОбъекта));
	КонецЦикла;
	
	СтруктураОбъектов = ОбщиеРеквизитыОбъектов(ТипыИзменяемыхОбъектов);
	
	Результат = "";
	ПсевдонимТаблицы = "ПсевдонимЗаданнойТаблицы";
	Для Каждого ОбъектМетаданных Из ОбъектыМетаданных Цикл
		
		Если Не ПустаяСтрока(Результат) Тогда
			Результат = Результат + Символы.ПС + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС + Символы.ПС;
		КонецЕсли;
		
		ТекстЗапроса = "";
		
		Для Каждого ИмяРеквизита Из СтруктураОбъектов.Реквизиты Цикл
			Если Не ПустаяСтрока(ТекстЗапроса) Тогда
				ТекстЗапроса = ТекстЗапроса + "," + Символы.ПС;
			КонецЕсли;
			ТекстЗапроса = ТекстЗапроса + ПсевдонимТаблицы + "." + ИмяРеквизита + " КАК " + ИмяРеквизита;
		КонецЦикла;
		
		Для Каждого ТабличнаяЧасть Из СтруктураОбъектов.ТабличныеЧасти Цикл
			ИмяТабличнойЧасти = ТабличнаяЧасть.Ключ;
			ТекстЗапроса = ТекстЗапроса + "," + Символы.ПС + ПсевдонимТаблицы + "." + ИмяТабличнойЧасти + ".(";
			
			СтрокаРеквизитов = "НомерСтроки";
			РеквизитыТабличнойЧасти = ТабличнаяЧасть.Значение;
			Для Каждого ИмяРеквизита Из РеквизитыТабличнойЧасти Цикл
				Если Не ПустаяСтрока(СтрокаРеквизитов) Тогда
					СтрокаРеквизитов = СтрокаРеквизитов + "," + Символы.ПС;
				КонецЕсли;
				СтрокаРеквизитов = СтрокаРеквизитов + ИмяТабличнойЧасти + "." +  ИмяРеквизита + " КАК " + ИмяРеквизита; //@query-part-2
			КонецЦикла;
			ТекстЗапроса = ТекстЗапроса + СтрокаРеквизитов +"
			|)";
		КонецЦикла;
		
		ТекстЗапроса = "ВЫБРАТЬ " + ?(ОграничитьВыборку, "ПЕРВЫЕ 1001 ", "") //@query-part
			+ ТекстЗапроса + Символы.ПС + "

			|ИЗ
			|	"+ ОбъектМетаданных.ПолноеИмя() + " КАК " + ПсевдонимТаблицы;
			
		Результат = Результат + ТекстЗапроса;
	КонецЦикла;
		
		
	Возврат Результат;
	
КонецФункции

Функция ОбщиеРеквизитыОбъектов(ТипыОбъектов) Экспорт
	
	ОбъектыМетаданных = Новый Массив;
	Для Каждого ИмяОбъекта Из СтрРазделить(ТипыОбъектов, ",", Ложь) Цикл
		ОбъектыМетаданных.Добавить(Метаданные.НайтиПоПолномуИмени(ИмяОбъекта));
	КонецЦикла;
	
	Результат = Новый Структура;
	Результат.Вставить("Реквизиты", Новый Массив);
	Результат.Вставить("ТабличныеЧасти", Новый Структура);
	
	Если ОбъектыМетаданных.Количество() = 0 Тогда
		Возврат Результат;
	КонецЕсли;	
		
	ОбщийСписокРеквизитов = СписокЭлементов(ОбъектыМетаданных[0].Реквизиты, Ложь);
	Для Индекс = 1 По ОбъектыМетаданных.Количество() - 1 Цикл
		ОбщийСписокРеквизитов = ПересечениеРеквизитов(ОбщийСписокРеквизитов, ОбъектыМетаданных[Индекс].Реквизиты);
	КонецЦикла;
	
	СтандартныеРеквизиты = ОбъектыМетаданных[0].СтандартныеРеквизиты;
	Для Индекс = 1 По ОбъектыМетаданных.Количество() - 1 Цикл
		СтандартныеРеквизиты = ПересечениеРеквизитов(СтандартныеРеквизиты, ОбъектыМетаданных[Индекс].СтандартныеРеквизиты);
	КонецЦикла;
	Для Каждого Реквизит Из СтандартныеРеквизиты Цикл
		ОбщийСписокРеквизитов.Добавить(Реквизит);
	КонецЦикла;
	
	Результат.Реквизиты = СписокЭлементов(ОбщийСписокРеквизитов);
	
	ТабличныеЧасти = СписокЭлементов(ОбъектыМетаданных[0].ТабличныеЧасти);
	Для Индекс = 1 По ОбъектыМетаданных.Количество() - 1 Цикл
		ТабличныеЧасти = ПересечениеМножеств(ТабличныеЧасти, СписокЭлементов(ОбъектыМетаданных[Индекс].ТабличныеЧасти));
	КонецЦикла;
	
	Для Каждого ИмяТабличнойЧасти Из ТабличныеЧасти Цикл
		РеквизитыТабличнойЧасти = СписокЭлементов(ОбъектыМетаданных[0].ТабличныеЧасти[ИмяТабличнойЧасти].Реквизиты, Ложь);
		Для Индекс = 1 По ОбъектыМетаданных.Количество() - 1 Цикл
			РеквизитыТабличнойЧасти = ПересечениеРеквизитов(РеквизитыТабличнойЧасти, ОбъектыМетаданных[Индекс].ТабличныеЧасти[ИмяТабличнойЧасти].Реквизиты);
		КонецЦикла;
		Если РеквизитыТабличнойЧасти.Количество() > 0 Тогда
			Результат.ТабличныеЧасти.Вставить(ИмяТабличнойЧасти, СписокЭлементов(РеквизитыТабличнойЧасти));
		КонецЕсли;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции


// Параметры:
//   Коллекция - Массив из ОбъектМетаданныхРеквизит
//             - Массив из ОбъектМетаданныхТабличнаяЧасть
//   ТолькоИмена - Булево
// Возвращаемое значение:
//   Массив
//
Функция СписокЭлементов(Коллекция, ТолькоИмена = Истина)
	Результат = Новый Массив;
	Для Каждого Элемент Из Коллекция Цикл
		Если ТолькоИмена Тогда
			Результат.Добавить(Элемент.Имя);
		Иначе
			Результат.Добавить(Элемент);
		КонецЕсли;
	КонецЦикла;
	Возврат Результат;
КонецФункции

Функция ПересечениеМножеств(Множество1, Множество2) Экспорт
	
	Результат = Новый Массив;
	
	Для Каждого Элемент Из Множество2 Цикл
		Индекс = Множество1.Найти(Элемент);
		Если Индекс <> Неопределено Тогда
			Результат.Добавить(Элемент);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция ПересечениеРеквизитов(КоллекцияРеквизитов1, КоллекцияРеквизитов2)
	
	Результат = Новый Массив;
	
	Для Каждого Реквизит2 Из КоллекцияРеквизитов2 Цикл
		Для Каждого Реквизит1 Из КоллекцияРеквизитов1 Цикл
			Если Реквизит1.Имя = Реквизит2.Имя 
				И (Реквизит1.Тип = Реквизит2.Тип Или Реквизит1.Имя = "Ссылка") Тогда
				Результат.Добавить(Реквизит1);
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Для внутреннего использования.
Функция СхемаКомпоновкиДанных(ТекстЗапроса) Экспорт
	СхемаКомпоновкиДанных = Новый СхемаКомпоновкиДанных;
	
	ИсточникДанных = СхемаКомпоновкиДанных.ИсточникиДанных.Добавить();
	ИсточникДанных.Имя = "ИсточникДанных1";
	ИсточникДанных.ТипИсточникаДанных = "local";
	
	НаборДанных = СхемаКомпоновкиДанных.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
	НаборДанных.ИсточникДанных = "ИсточникДанных1";
	НаборДанных.АвтоЗаполнениеДоступныхПолей = Истина;
	НаборДанных.Запрос = ТекстЗапроса;
	НаборДанных.Имя = "НаборДанных1";
	
	Возврат СхемаКомпоновкиДанных;
КонецФункции

// Для внутреннего использования.
Функция ИзменитьОбъекты(Параметры, АдресРезультата) Экспорт
	
	ОбрабатываемыеОбъекты = Параметры.ОбрабатываемыеОбъекты.Получить().Строки;
	ОбъектыДляИзменения   = Параметры.ОбъектыДляИзменения.Получить().Строки;
	
	РезультатИзменения = Новый Структура("ЕстьОшибки, СостояниеОбработки");
	РезультатИзменения.ЕстьОшибки         = Ложь;
	РезультатИзменения.СостояниеОбработки = Новый Соответствие;
	
	Если ОбрабатываемыеОбъекты = Неопределено Тогда
		ОбрабатываемыеОбъекты = Новый Массив;// Массив из ЛюбаяСсылка
		Для Каждого ИзменяемыйОбъект Из ОбъектыДляИзменения Цикл
			ОбрабатываемыеОбъекты.Добавить(ИзменяемыйОбъект);
		КонецЦикла;
	КонецЕсли;
	
	Если ОбрабатываемыеОбъекты.Количество() = 0 Тогда
		ПоместитьВоВременноеХранилище(РезультатИзменения, АдресРезультата);
		Возврат Неопределено;
	КонецЕсли;
	
	Если Параметры.ТипОперации = "ВыполнитьАлгоритм" И РазделениеВключено() Тогда
		ПоместитьВоВременноеХранилище(РезультатИзменения, АдресРезультата);
		Возврат Неопределено;
	КонецЕсли;
	
	ОстанавливатьИзменениеПриОшибке = Параметры.ОстанавливатьИзменениеПриОшибке;
	Если ОстанавливатьИзменениеПриОшибке = Неопределено Тогда
		ОстанавливатьИзменениеПриОшибке = Параметры.ПрерыватьПриОшибке;
	КонецЕсли;
	
	ВыполнятьКодАлгоритмаВБезопасномРежиме = (Параметры.РежимВыполнения <> 1);
	
	ЭтоВнешняяОбработка = ЭтоВнешняяОбработка();
	Если ЭтоВнешняяОбработка Тогда
		Параметры.ВнешнийОтчетОбработка =
			?(ЗначениеЗаполнено(АдресДвоичныхДанныхВнешнейОбработки),
				ПолучитьИзВременногоХранилища(АдресДвоичныхДанныхВнешнейОбработки), Неопределено);
	КонецЕсли;
	
	Если Не ДоступныДлительныеОперации()
	 Или ЭтоВнешняяОбработка 
	   И ТипЗнч(Параметры.ВнешнийОтчетОбработка) <> Тип("ДвоичныеДанные") Тогда
		
		НастройкиИзменения = НастройкиИзменения(Параметры, ВыполнятьКодАлгоритмаВБезопасномРежиме);
		РезультатИзмененияПорцииОбъектов(ОбрабатываемыеОбъекты, РезультатИзменения, НастройкиИзменения);
	Иначе
		Возврат ВыполнитьИзменениеОбъектовВНесколькоПотоков(Параметры, ОбрабатываемыеОбъекты, РезультатИзменения,
			ОстанавливатьИзменениеПриОшибке, ВыполнятьКодАлгоритмаВБезопасномРежиме);
	КонецЕсли;
	
	ПоместитьВоВременноеХранилище(РезультатИзменения, АдресРезультата);
	
	Возврат Неопределено;
	
КонецФункции

// Параметры:
//   Блокировка - БлокировкаДанных 
//   Ссылка - ЛюбаяСсылка
//
Процедура ЗаблокироватьСсылку(Знач Блокировка, Знач Ссылка)
	
	ЗаблокироватьДанныеДляРедактирования(Ссылка);
	ЭлементБлокировки = Блокировка.Добавить(ВидОбъектаПоСсылке(Ссылка) + "." + Ссылка.Метаданные().Имя);
	ЭлементБлокировки.УстановитьЗначение("Ссылка", Ссылка);

КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//   * ИзменяемыеРеквизитыОбъекта - Массив из Строка 
//   * ИзменяемыеДопРеквизитыОбъекта - Соответствие
//   * ИзменяемыеДопСведенияОбъекта - Соответствие
//   * МассивЗаписейДопСведений - Массив из РегистрСведенийМенеджерЗаписи.ДополнительныеСведения
//
Функция ВнестиИзменения(Знач ДанныеОбъекта, Знач ИзменяемыйОбъект, Знач Параметры)
	
	Результат = Новый Структура;
	Результат.Вставить("ИзменяемыеРеквизитыОбъекта",    Новый Массив);
	Результат.Вставить("ИзменяемыеДопРеквизитыОбъекта", Новый Соответствие);
	Результат.Вставить("ИзменяемыеДопСведенияОбъекта",  Новый Соответствие);
	Результат.Вставить("МассивЗаписейДопСведений",      Новый Массив);
	
	ДополнительныеРеквизиты = Новый Массив;
	Для Каждого Операция Из Параметры.ИзменяемыеРеквизиты Цикл
		Если Операция.ВидОперации = 2 Тогда
			ДополнительныеРеквизиты.Добавить(Операция.Свойство);
		КонецЕсли;
	КонецЦикла;
	
	СвойстваДополнительныхРеквизитов = Неопределено;
	Если ДополнительныеРеквизиты.Количество() > 0 Тогда
		МодульОбщегоНазначения = ОбщийМодуль("ОбщегоНазначения");
		СвойстваДополнительныхРеквизитов = МодульОбщегоНазначения.ЗначенияРеквизитовОбъектов(ДополнительныеРеквизиты, "ТипЗначения, МногострочноеПолеВвода");
	КонецЕсли;
	
	// Выполнение операций изменения.
	Для Каждого Операция Из Параметры.ИзменяемыеРеквизиты Цикл
		
		Значение = ВычислитьВыражение(Операция.Значение, ИзменяемыйОбъект, Параметры.ДоступныеРеквизиты);
		Если Операция.ВидОперации = 1 Тогда // изменение реквизита
			
			Если ИзменяемыйОбъект[Операция.Имя] = Null Тогда
				Продолжить;
			КонецЕсли;
			
			ИзменяемыйОбъект[Операция.Имя] = Значение;
			Результат.ИзменяемыеРеквизитыОбъекта.Добавить(Операция.Имя);
			
		ИначеЕсли Операция.ВидОперации = 2 Тогда // изменение дополнительного реквизита
			
			Если Не СвойствоНужноИзменять(ИзменяемыйОбъект.Ссылка, Операция.Свойство, Параметры) Тогда
				Продолжить;
			КонецЕсли;
			
			НайденнаяСтрока   = ИзменяемыйОбъект.ДополнительныеРеквизиты.Найти(Операция.Свойство, "Свойство");
			СвойстваРеквизита = СвойстваДополнительныхРеквизитов[Операция.Свойство];
			СоставнойТип      = СвойстваРеквизита.ТипЗначения.Типы().Количество() > 1;
			Если (СоставнойТип И Значение <> Неопределено)
				Или (Не СоставнойТип И ЗначениеЗаполнено(Значение)) Тогда
				Если НайденнаяСтрока = Неопределено Тогда
					НайденнаяСтрока = ИзменяемыйОбъект.ДополнительныеРеквизиты.Добавить();
					НайденнаяСтрока.Свойство = Операция.Свойство;
				КонецЕсли;
				НайденнаяСтрока.Значение = Значение;
				
				МодульУправлениеСвойствамиСлужебный = ОбщийМодуль("УправлениеСвойствамиСлужебный");
				Если МодульУправлениеСвойствамиСлужебный.ИспользоватьНеограниченнуюСтроку(СвойстваРеквизита.ТипЗначения, СвойстваРеквизита.МногострочноеПолеВвода) Тогда
					НайденнаяСтрока.ТекстоваяСтрока = Значение;
				КонецЕсли;
			Иначе
				Если НайденнаяСтрока <> Неопределено Тогда
					ИзменяемыйОбъект.ДополнительныеРеквизиты.Удалить(НайденнаяСтрока);
				КонецЕсли;
			КонецЕсли;
			
			ИмяРеквизитаФормы = ПрефиксИмениДопРеквизита() + СтрЗаменить(Строка(Операция.Свойство.УникальныйИдентификатор()), "-", "_");
			Результат.ИзменяемыеДопРеквизитыОбъекта.Вставить(ИмяРеквизитаФормы, Значение);
			
		ИначеЕсли Операция.ВидОперации = 3 Тогда // изменение дополнительного сведения
			
			Если Не СвойствоНужноИзменять(ИзменяемыйОбъект.Ссылка, Операция.Свойство, Параметры) Тогда
				Продолжить;
			КонецЕсли;
			
			МенеджерЗаписи = РегистрыСведений["ДополнительныеСведения"].СоздатьМенеджерЗаписи();
			МенеджерЗаписи.Объект = ИзменяемыйОбъект.Ссылка;
			МенеджерЗаписи.Свойство = Операция.Свойство;
			МенеджерЗаписи.Значение = Значение;
			Результат.МассивЗаписейДопСведений.Добавить(МенеджерЗаписи);
			
			ИмяРеквизитаФормы = ПрефиксИмениДопСведения() + СтрЗаменить(Строка(Операция.Свойство.УникальныйИдентификатор()), "-", "_");
			Результат.ИзменяемыеДопСведенияОбъекта.Вставить(ИмяРеквизитаФормы, Значение);
			
		КонецЕсли;
		
	КонецЦикла;
	
	Если Параметры.ИзменяемыеТабличныеЧасти.Количество() > 0 Тогда
		ВнестиИзмененияВТабличныеЧасти(ИзменяемыйОбъект, ДанныеОбъекта, Параметры.ИзменяемыеТабличныеЧасти);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Процедура ВыполнитьКодАлгоритма(Знач Объект, Знач КодАлгоритма, Знач ВыполнитьВБезопасномРежиме)
	
	Если ВыполнитьВБезопасномРежиме Или НЕ ПравоДоступа("Администрирование", Метаданные) Тогда
		КодАлгоритма = "Объект = Параметры;
		|" + КодАлгоритма;
		ВыполнитьВБезопасномРежиме(КодАлгоритма, Объект);
	Иначе
		Выполнить КодАлгоритма; // АПК:487 Для "ремонтных" сценариев допускается выполнение кода Администратором.
	КонецЕсли;
	
КонецПроцедуры

Функция РазделениеВключено()
	
	ДоступнаРаботаВМоделиСервиса = Метаданные.ФункциональныеОпции.Найти("РаботаВМоделиСервиса");
	Если ДоступнаРаботаВМоделиСервиса <> Неопределено Тогда
		ИмяОпции = "РаботаВМоделиСервиса";
		Возврат ЭтоРазделеннаяКонфигурация() И ПолучитьФункциональнуюОпцию(ИмяОпции);
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Возвращает признак наличия в конфигурации общих реквизитов-разделителей.
//
// Возвращаемое значение:
//   Булево
//
Функция ЭтоРазделеннаяКонфигурация()
	
	ЕстьРазделители = Ложь;
	Для каждого ОбщийРеквизит Из Метаданные.ОбщиеРеквизиты Цикл
		Если ОбщийРеквизит.РазделениеДанных = Метаданные.СвойстваОбъектов.РазделениеДанныхОбщегоРеквизита.Разделять Тогда
			ЕстьРазделители = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ЕстьРазделители;
	
КонецФункции

Функция ОпределитьРежимЗаписи(Знач ИзменяемыйОбъект, Знач ЭтоДокумент, Знач РежимРазработчика)
	
	РежимЗаписи = Неопределено;
	Если РежимРазработчика Тогда
		РежимЗаписи = Неопределено;
		ИзменяемыйОбъект.ОбменДанными.Загрузка = Истина;
	ИначеЕсли ЭтоДокумент Тогда
		РежимЗаписи = РежимЗаписиДокумента.Запись;
		Если ИзменяемыйОбъект.Проведен Тогда
			РежимЗаписи = РежимЗаписиДокумента.Проведение;
		ИначеЕсли ИзменяемыйОбъект.Метаданные().Проведение = Метаданные.СвойстваОбъектов.Проведение.Разрешить Тогда
			РежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения;
		КонецЕсли;
	КонецЕсли;
	Возврат РежимЗаписи;

КонецФункции

Функция СвойствоНужноИзменять(Ссылка, Свойство, Параметры)
	
	Если ПодсистемаСуществует("СтандартныеПодсистемы.Свойства") Тогда
		МодульУправлениеСвойствами = ОбщийМодуль("УправлениеСвойствами");
		Если МодульУправлениеСвойствами = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	ВидОбъектаПоСсылке = ВидОбъектаПоСсылке(Ссылка);
	Если (ВидОбъектаПоСсылке = "Справочник" ИЛИ ВидОбъектаПоСсылке = "ПланВидовХарактеристик")
		И ОбъектЯвляетсяГруппой(Ссылка) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если НЕ МодульУправлениеСвойствами.ПроверитьСвойствоУОбъекта(Ссылка, Свойство) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

Функция ТекстОшибокПроверкиЗаполнения()
	
	Результат = "";
	МассивСообщений = ПолучитьСообщенияПользователю(Истина);
	
	Для Каждого СообщениеПользователю Из МассивСообщений Цикл
		Результат = Результат + СообщениеПользователю.Текст + Символы.ПС;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Процедура ЗаполнитьРезультатИзменения(Результат, Ссылка, СообщениеОбОшибке)
	
	СостояниеИзменения = Новый Структура;
	СостояниеИзменения.Вставить("КодОшибки", "Ошибка");
	СостояниеИзменения.Вставить("СообщениеОбОшибке", СообщениеОбОшибке);
	
	Результат.СостояниеОбработки.Вставить(Ссылка, СостояниеИзменения);
	Результат.ЕстьОшибки = Истина;
	
КонецПроцедуры

Процедура ЗаполнитьРезультатИзмененияДополнительныхСвойств(Результат, Ссылка, ИзменяемыйОбъект, Изменения = Неопределено)
	
	СостояниеИзменения = Новый Структура;
	СостояниеИзменения.Вставить("КодОшибки", "");
	СостояниеИзменения.Вставить("СообщениеОбОшибке", "");
	СостояниеИзменения.Вставить("ЗначенияИзмененныхРеквизитов", Новый Соответствие);
	Если Изменения <> Неопределено Тогда
		Для Каждого ИмяРеквизита Из Изменения.ИзменяемыеРеквизитыОбъекта Цикл
			СостояниеИзменения.ЗначенияИзмененныхРеквизитов.Вставить(ИмяРеквизита, ИзменяемыйОбъект[ИмяРеквизита]);
		КонецЦикла;
	КонецЕсли;
	СостояниеИзменения.Вставить("ЗначенияИзмененныхДопРеквизитов", 
		?(Изменения <> Неопределено, Изменения.ИзменяемыеДопРеквизитыОбъекта, Изменения));
	СостояниеИзменения.Вставить("ЗначенияИзмененныхДопСведений", 
		?(Изменения <> Неопределено, Изменения.ИзменяемыеДопСведенияОбъекта, Изменения));
	
	Результат.СостояниеОбработки.Вставить(Ссылка, СостояниеИзменения);
	
КонецПроцедуры

Функция ПрефиксИмениДопРеквизита()
	Возврат "ДопРеквизит_";
КонецФункции

Функция ПрефиксИмениДопСведения()
	Возврат "ДопСведение_";
КонецФункции

Процедура ВнестиИзмененияВТабличныеЧасти(ИзменяемыйОбъект, ДанныеОбъекта, ИзмененияВТабличныхЧастях)
	
	Для Каждого ИзмененияТабличнойЧасти Из ИзмененияВТабличныхЧастях Цикл
		ИмяТаблицы = ИзмененияТабличнойЧасти.Ключ;
		ИзменяемыеРеквизиты = ИзмененияТабличнойЧасти.Значение;
		Для Каждого СтрокаТаблицы Из ИзменяемыйОбъект[ИмяТаблицы] Цикл
			Если СтрокаСоответствуетОтбору(СтрокаТаблицы, ДанныеОбъекта, ИмяТаблицы) Тогда
				Для Каждого ИзменяемыйРеквизит Из ИзменяемыеРеквизиты Цикл
					СтрокаТаблицы[ИзменяемыйРеквизит.Имя] = ИзменяемыйРеквизит.Значение;
				КонецЦикла;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//   СтрокаТаблицы - СтрокаТаблицыЗначений:
//   * НомерСтроки - Число
//   ДанныеОбъекта - ЛюбаяСсылка
//   ИмяТаблицы - Строка
//
Функция СтрокаСоответствуетОтбору(СтрокаТаблицы, ДанныеОбъекта, ИмяТаблицы)
	
	Строки = ?(ТипЗнч(ДанныеОбъекта) = Тип("СтрокаДереваЗначений"), 
		ДанныеОбъекта.Строки, ДанныеОбъекта.ТабличныеЧасти.Строки);
	
	Возврат Строки.НайтиСтроки(Новый Структура(ИмяТаблицы + "НомерСтроки", СтрокаТаблицы.НомерСтроки)).Количество() = 1;
	
КонецФункции

Процедура ЗаполнитьКоллекциюДоступныхДляИзмененияОбъектов(ДоступныеОбъекты, ПоказыватьСкрытые) Экспорт

	КоллекцииОбъектовМетаданных = Новый Массив;
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.Справочники);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.Документы);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.БизнесПроцессы);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.Задачи);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.ПланыВидовРасчета);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.ПланыВидовХарактеристик);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.ПланыСчетов);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.ПланыОбмена);
	
	ПрефиксУдаляемыхОбъектов = "удалить";
	УдаляемыеОбъекты = Новый СписокЗначений;
	
	МенеджерыОбъектов = Неопределено;
	МодульОбщегоНазначения = Неопределено;
	Если ВерсияБСПСоответствуетТребованиям() Тогда
		МенеджерыОбъектов = МенеджерыОбъектовДляРедактированияРеквизитов();
		МодульОбщегоНазначения = ОбщийМодуль("ОбщегоНазначения");
	КонецЕсли;
	
	Для Каждого КоллекцияОбъектовМетаданных Из КоллекцииОбъектовМетаданных Цикл
		Для Каждого ОбъектМетаданных Из КоллекцияОбъектовМетаданных Цикл
			Если МодульОбщегоНазначения <> Неопределено
			   И Не МодульОбщегоНазначения.ОбъектМетаданныхДоступенПоФункциональнымОпциям(ОбъектМетаданных) Тогда
				Продолжить;
			КонецЕсли;
			Если Не ПоказыватьСкрытые Тогда
				Если СтрНачинаетсяС(НРег(ОбъектМетаданных.Имя),ПрефиксУдаляемыхОбъектов)
					Или ЭтоСлужебныйОбъект(ОбъектМетаданных, МенеджерыОбъектов) Тогда
					Продолжить;
				КонецЕсли;
			КонецЕсли;
			
			Если ПравоДоступа("Изменение", ОбъектМетаданных) Тогда
				Если СтрНачинаетсяС(НРег(ОбъектМетаданных.Имя),ПрефиксУдаляемыхОбъектов) Тогда
					УдаляемыеОбъекты.Добавить(ОбъектМетаданных.ПолноеИмя(), ОбъектМетаданных.Представление());
				Иначе 
					ДоступныеОбъекты.Добавить(ОбъектМетаданных.ПолноеИмя(), ОбъектМетаданных.Представление());
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	ДоступныеОбъекты.СортироватьПоПредставлению();
	УдаляемыеОбъекты.СортироватьПоПредставлению();
	
	Для Каждого Элемент Из УдаляемыеОбъекты Цикл
		ДоступныеОбъекты.Добавить(Элемент.Значение, Элемент.Представление);
	КонецЦикла;
	
КонецПроцедуры

Функция ЭтоСлужебныйОбъект(Знач ОбъектМетаданных, Знач МенеджерыОбъектов)
	
	НастройкиРедактированияРеквизитов = НастройкиРедактированияРеквизитов(ОбъектМетаданных, МенеджерыОбъектов);
	
	Редактируемые = НастройкиРедактированияРеквизитов.Редактируемые;
	НеРедактируемые = НастройкиРедактированияРеквизитов.НеРедактируемые;
	
	Если ТипЗнч(НеРедактируемые) = Тип("Массив") И НеРедактируемые.Найти("*") <> Неопределено
		Или ТипЗнч(Редактируемые) = Тип("Массив") И Не ЗначениеЗаполнено(Редактируемые) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

Функция НастройкиРедактированияРеквизитов(ОбъектМетаданных, МенеджерыОбъектов = Null) Экспорт
	
	Если МенеджерыОбъектов = Null Тогда
		МенеджерыОбъектов = МенеджерыОбъектовДляРедактированияРеквизитов();
	КонецЕсли;
	
	Редактируемые   = Неопределено;
	НеРедактируемые = Неопределено;
	
	Если МенеджерыОбъектов <> Неопределено Тогда
		ДоступныеМетоды = МетодыМенеджераОбъектаДляРедактированияРеквизитов(ОбъектМетаданных.ПолноеИмя(), МенеджерыОбъектов);
		Если ТипЗнч(ДоступныеМетоды) = Тип("Массив") И (ДоступныеМетоды.Количество() = 0
			Или ДоступныеМетоды.Найти("РеквизитыРедактируемыеВГрупповойОбработке") <> Неопределено) Тогда
				МенеджерОбъекта = МенеджерОбъектаПоПолномуИмени(ОбъектМетаданных.ПолноеИмя());
				Редактируемые = МенеджерОбъекта.РеквизитыРедактируемыеВГрупповойОбработке();
		КонецЕсли;
	Иначе
		// В конфигурациях без БСП или на старых версиях БСП, пытаемся определить,
		// есть ли у объекта редактируемые реквизиты.
		МенеджерОбъекта = МенеджерОбъектаПоПолномуИмени(ОбъектМетаданных.ПолноеИмя());
		Попытка
			Редактируемые = МенеджерОбъекта.РеквизитыРедактируемыеВГрупповойОбработке();
		Исключение
			// метод не найден
			Редактируемые = Неопределено;
		КонецПопытки;
	КонецЕсли;
	
	Если МенеджерыОбъектов <> Неопределено Тогда
		Если ТипЗнч(ДоступныеМетоды) = Тип("Массив") И (ДоступныеМетоды.Количество() = 0
			Или ДоступныеМетоды.Найти("РеквизитыНеРедактируемыеВГрупповойОбработке") <> Неопределено) Тогда
				Если МенеджерОбъекта = Неопределено Тогда
					МенеджерОбъекта = МенеджерОбъектаПоПолномуИмени(ОбъектМетаданных.ПолноеИмя());
				КонецЕсли;
				НеРедактируемые = МенеджерОбъекта.РеквизитыНеРедактируемыеВГрупповойОбработке();
		КонецЕсли;
		
	Иначе
		// В конфигурациях без БСП или на старых версиях БСП, пытаемся определить,
		// есть ли у объекта нередактируемые реквизиты.
		Попытка
			НеРедактируемые = МенеджерОбъекта.РеквизитыНеРедактируемыеВГрупповойОбработке();
		Исключение
			// метод не найден
			НеРедактируемые = Неопределено;
		КонецПопытки;
	КонецЕсли;
	
	Если ВерсияБСПСоответствуетТребованиям() Тогда
		МодульИнтеграцияПодсистемБСП = ОбщийМодуль("ИнтеграцияПодсистемБСП");
		МодульГрупповоеИзменениеОбъектовПереопределяемый = ОбщийМодуль("ГрупповоеИзменениеОбъектовПереопределяемый");
		
		МодульИнтеграцияПодсистемБСП.ПриОпределенииРедактируемыхРеквизитовОбъекта(
			ОбъектМетаданных, Редактируемые, НеРедактируемые);
		
		МодульГрупповоеИзменениеОбъектовПереопределяемый.ПриОпределенииРедактируемыхРеквизитовОбъекта(
			ОбъектМетаданных, Редактируемые, НеРедактируемые);
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("Редактируемые", Редактируемые);
	Результат.Вставить("НеРедактируемые", НеРедактируемые);
	
	Возврат Результат;
	
КонецФункции

Функция МетодыМенеджераОбъектаДляРедактированияРеквизитов(ИмяОбъекта, МенеджерыОбъектов)
	
	СведенияОМенеджереОбъекта = МенеджерыОбъектов[ИмяОбъекта];
	Если СведенияОМенеджереОбъекта = Неопределено Тогда
		Возврат "НеПоддерживается";
	КонецЕсли;
	ДоступныеМетоды = СтрРазделить(СведенияОМенеджереОбъекта, Символы.ПС, Ложь);
	Возврат ДоступныеМетоды;
	
КонецФункции

Функция МенеджерыОбъектовДляРедактированияРеквизитов()
	
	ОбъектыСЗаблокированнымиРеквизитами = Новый Соответствие;
	Если Не ПодсистемаСуществует("СтандартныеПодсистемы.БазоваяФункциональность") Тогда
		Возврат ОбъектыСЗаблокированнымиРеквизитами;
	КонецЕсли;
	
	МодульИнтеграцияПодсистемБСП = ОбщийМодуль("ИнтеграцияПодсистемБСП");
	МодульГрупповоеИзменениеОбъектовПереопределяемый = ОбщийМодуль("ГрупповоеИзменениеОбъектовПереопределяемый");
	Если МодульИнтеграцияПодсистемБСП = Неопределено Или МодульГрупповоеИзменениеОбъектовПереопределяемый = Неопределено Тогда
		Возврат ОбъектыСЗаблокированнымиРеквизитами;
	КонецЕсли;
	
	МодульИнтеграцияПодсистемБСП.ПриОпределенииОбъектовСРедактируемымиРеквизитами(ОбъектыСЗаблокированнымиРеквизитами);
	МодульГрупповоеИзменениеОбъектовПереопределяемый.ПриОпределенииОбъектовСРедактируемымиРеквизитами(ОбъектыСЗаблокированнымиРеквизитами);
	
	Возврат ОбъектыСЗаблокированнымиРеквизитами;
	
КонецФункции

Функция ВерсияБСПСоответствуетТребованиям() Экспорт
	
	Попытка
		МодульСтандартныеПодсистемыСервер = ОбщийМодуль("СтандартныеПодсистемыСервер");
	Исключение
		// Модуль не существует
		МодульСтандартныеПодсистемыСервер = Неопределено;
	КонецПопытки;
	Если МодульСтандартныеПодсистемыСервер = Неопределено Тогда 
		Возврат Ложь;
	КонецЕсли;
	
	ВерсияБСП = МодульСтандартныеПодсистемыСервер.ВерсияБиблиотеки();
	Возврат СравнитьВерсии(ВерсияБСП, "3.1.8.240") >= 0;
	
КонецФункции

Функция ДоступныДлительныеОперации() Экспорт
	Возврат ВерсияБСПСоответствуетТребованиям() И ПодсистемаСуществует("СтандартныеПодсистемы.БазоваяФункциональность");
КонецФункции

// Сравнить две строки версий.
//
// Параметры:
//  СтрокаВерсии1  - Строка - номер версии в формате РР.{П|ПП}.ЗЗ.СС.
//  СтрокаВерсии2  - Строка - второй сравниваемый номер версии.
//
// Возвращаемое значение:
//   Число   - больше 0, если СтрокаВерсии1 > СтрокаВерсии2; 0, если версии равны.
//
Функция СравнитьВерсии(Знач СтрокаВерсии1, Знач СтрокаВерсии2) Экспорт
	
	Строка1 = ?(ПустаяСтрока(СтрокаВерсии1), "0.0.0.0", СтрокаВерсии1);
	Строка2 = ?(ПустаяСтрока(СтрокаВерсии2), "0.0.0.0", СтрокаВерсии2);
	Версия1 = СтрРазделить(Строка1, ".");
	Если Версия1.Количество() <> 4 Тогда
		ВызватьИсключение ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неправильный формат параметра %1: %2'"), "СтрокаВерсии1", СтрокаВерсии1);
	КонецЕсли;
	Версия2 = СтрРазделить(Строка2, ".");
	Если Версия2.Количество() <> 4 Тогда
		ВызватьИсключение ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неправильный формат параметра %1: %2'"), "СтрокаВерсии2", СтрокаВерсии2);
	КонецЕсли;
	
	Результат = 0;
	Для Разряд = 0 По 3 Цикл
		Результат = Число(Версия1[Разряд]) - Число(Версия2[Разряд]);
		Если Результат <> 0 Тогда
			Возврат Результат;
		КонецЕсли;
	КонецЦикла;
	Возврат Результат;
	
КонецФункции

// Возвращает менеджер объекта по полному имени объекта метаданных.
// Ограничение: не обрабатываются точки маршрутов бизнес-процессов.
//
// Параметры:
//  ПолноеИмя - Строка - полное имя объекта метаданных. Пример: "Справочник.Организации".
//
// Возвращаемое значение:
//  СправочникМенеджер, ДокументМенеджер, ОбработкаМенеджер, РегистрСведенийМенеджер - менеджер объекта.
// 
// Пример:
//  МенеджерСправочника = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени("Справочник.Организации");
//  ПустаяСсылка = МенеджерСправочника.ПустаяСсылка();
//
Функция МенеджерОбъектаПоПолномуИмени(ПолноеИмя) Экспорт
	Перем КлассОМ, ИмяОМ, Менеджер;
	
	ЧастиИмени = СтрРазделить(ПолноеИмя, ".");
	
	Если ЧастиИмени.Количество() >= 2 Тогда
		КлассОМ = ЧастиИмени[0];
		ИмяОМ  = ЧастиИмени[1];
	КонецЕсли;
	
	Если      ВРег(КлассОМ) = "ПЛАНОБМЕНА" Тогда
		Менеджер = ПланыОбмена;
		
	ИначеЕсли ВРег(КлассОМ) = "СПРАВОЧНИК" Тогда
		Менеджер = Справочники;
		
	ИначеЕсли ВРег(КлассОМ) = "ДОКУМЕНТ" Тогда
		Менеджер = Документы;
		
	ИначеЕсли ВРег(КлассОМ) = "ЖУРНАЛДОКУМЕНТОВ" Тогда
		Менеджер = ЖурналыДокументов;
		
	ИначеЕсли ВРег(КлассОМ) = "ПЕРЕЧИСЛЕНИЕ" Тогда
		Менеджер = Перечисления;
		
	ИначеЕсли ВРег(КлассОМ) = "ОТЧЕТ" Тогда
		Менеджер = Отчеты;
		
	ИначеЕсли ВРег(КлассОМ) = "ОБРАБОТКА" Тогда
		Менеджер = Обработки;
		
	ИначеЕсли ВРег(КлассОМ) = "ПЛАНВИДОВХАРАКТЕРИСТИК" Тогда
		Менеджер = ПланыВидовХарактеристик;
		
	ИначеЕсли ВРег(КлассОМ) = "ПЛАНСЧЕТОВ" Тогда
		Менеджер = ПланыСчетов;
		
	ИначеЕсли ВРег(КлассОМ) = "ПЛАНВИДОВРАСЧЕТА" Тогда
		Менеджер = ПланыВидовРасчета;
		
	ИначеЕсли ВРег(КлассОМ) = "РЕГИСТРСВЕДЕНИЙ" Тогда
		Менеджер = РегистрыСведений;
		
	ИначеЕсли ВРег(КлассОМ) = "РЕГИСТРНАКОПЛЕНИЯ" Тогда
		Менеджер = РегистрыНакопления;
		
	ИначеЕсли ВРег(КлассОМ) = "РЕГИСТРБУХГАЛТЕРИИ" Тогда
		Менеджер = РегистрыБухгалтерии;
		
	ИначеЕсли ВРег(КлассОМ) = "РЕГИСТРРАСЧЕТА" Тогда
		Если ЧастиИмени.Количество() = 2 Тогда
			// Регистр расчета
			Менеджер = РегистрыРасчета;
		Иначе
			КлассПодчиненногоОМ = ЧастиИмени[2];
			ИмяПодчиненногоОМ = ЧастиИмени[3];
			Если ВРег(КлассПодчиненногоОМ) = "ПЕРЕРАСЧЕТ" Тогда
				// Перерасчет
				Попытка
					Менеджер = РегистрыРасчета[ИмяОМ].Перерасчеты;
					ИмяОм = ИмяПодчиненногоОМ;
				Исключение
					Менеджер = Неопределено;
				КонецПопытки;
			КонецЕсли;
		КонецЕсли;
		
	ИначеЕсли ВРег(КлассОМ) = "БИЗНЕСПРОЦЕСС" Тогда
		Менеджер = БизнесПроцессы;
		
	ИначеЕсли ВРег(КлассОМ) = "ЗАДАЧА" Тогда
		Менеджер = Задачи;
		
	ИначеЕсли ВРег(КлассОМ) = "КОНСТАНТА" Тогда
		Менеджер = Константы;
		
	ИначеЕсли ВРег(КлассОМ) = "ПОСЛЕДОВАТЕЛЬНОСТЬ" Тогда
		Менеджер = Последовательности;
	КонецЕсли;
	
	Если Менеджер <> Неопределено Тогда
		Попытка
			Возврат Менеджер[ИмяОМ];
		Исключение
			Менеджер = Неопределено;
		КонецПопытки;
	КонецЕсли;
	
	ВызватьИсключение ПодставитьПараметрыВСтроку(НСтр("ru = 'Неизвестный тип объекта метаданных ""%1""'"), ПолноеИмя);
	
КонецФункции

Функция ВычислитьВыражение(Знач Выражение, Объект, ДоступныеРеквизиты)
	
	Если Не(ТипЗнч(Выражение) = Тип("Строка") И СтрНачинаетсяС(Выражение, "=")) Тогда
		Возврат Выражение;
	КонецЕсли;
		
	Если СтрНачинаетсяС(Выражение, "'=") Тогда
		Возврат Сред(Выражение, 2);
	КонецЕсли;
	
	Выражение = Сред(Выражение, 2);
	
	Для Каждого ОписаниеРеквизита Из ДоступныеРеквизиты Цикл
		Если СтрНайти(Выражение, "[" + ОписаниеРеквизита.Представление + "]") = 0 Тогда
			Продолжить;
		КонецЕсли;
		
		Значение = "";
		Если ОписаниеРеквизита.ВидОперации = 1 Тогда
			Значение = Объект[ОписаниеРеквизита.Имя];
		Иначе
			МодульУправлениеСвойствами = ОбщийМодуль("УправлениеСвойствами");
			СписокСвойств = Новый Массив;
			СписокСвойств.Добавить(ОписаниеРеквизита.Свойство);
			ЗначенияСвойств = МодульУправлениеСвойствами.ЗначенияСвойств(Объект.Ссылка, Истина, Истина, СписокСвойств);
			Для Каждого СтрокаТаблицы Из ЗначенияСвойств.НайтиСтроки(Новый Структура("Свойство", ОписаниеРеквизита.Свойство)) Цикл
				Значение = СтрокаТаблицы.Значение;
			КонецЦикла;
		КонецЕсли;
		
		Выражение = СтрЗаменить(Выражение, "[" + ОписаниеРеквизита.Представление + "]", """" 
			+ СтрЗаменить(СтрЗаменить(Значение, """", """"""), Символы.ПС, Символы.ПС + "|") + """");
	КонецЦикла;
	
	Возврат ВычислитьВБезопасномРежиме(Выражение);
	
КонецФункции

Процедура ОтключитьОбновлениеКлючейДоступа(Отключить, ПланироватьОбновление = Истина)
	
	Если Не ВерсияБСПСоответствуетТребованиям() Тогда
		Возврат;
	КонецЕсли;
	
	Если ПодсистемаСуществует("СтандартныеПодсистемы.Пользователи") Тогда
		МодульПользователи = ОбщийМодуль("Пользователи");
		Если Не МодульПользователи.ЭтоПолноправныйПользователь() Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Отключить, ПланироватьОбновление);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции из базовой функциональности для обеспечения автономности.

// Функция ВидОбъектаПоСсылке возвращает имя вида объектов метаданных
// по ссылке на объект.
//
// Не обрабатываются точки маршрутов бизнес-процессов.
//
// Параметры:
//  Ссылка - ЛюбаяСсылка
//
// Возвращаемое значение:
//  Строка
//
Функция ВидОбъектаПоСсылке(Ссылка) Экспорт
	
	Возврат ВидОбъектаПоТипу(ТипЗнч(Ссылка));
	
КонецФункции 

// Функция возвращает имя вида объектов метаданных по типу объекта.
//
// Не обрабатываются точки маршрутов бизнес-процессов.
//
// Параметры:
//  Тип       - тип прикладного объекта, определенный в конфигурации.
//
// Возвращаемое значение:
//  Строка       - имя вида объектов метаданных, например, "Справочник", "Документ" ...
// 
Функция ВидОбъектаПоТипу(Тип) Экспорт
	
	Если Справочники.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "Справочник";
	
	ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "Документ";
	
	ИначеЕсли БизнесПроцессы.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "БизнесПроцесс";
	
	ИначеЕсли ПланыВидовХарактеристик.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "ПланВидовХарактеристик";
	
	ИначеЕсли ПланыСчетов.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "ПланСчетов";
	
	ИначеЕсли ПланыВидовРасчета.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "ПланВидовРасчета";
	
	ИначеЕсли Задачи.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "Задача";
	
	ИначеЕсли ПланыОбмена.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "ПланОбмена";
	
	ИначеЕсли Перечисления.ТипВсеСсылки().СодержитТип(Тип) Тогда
		Возврат "Перечисление";
	
	Иначе
		ВызватьИсключение ПодставитьПараметрыВСтроку(НСтр("ru = 'Неверный тип значения параметра (%1)'"), Строка(Тип));
	
	КонецЕсли;
	
КонецФункции 

// Проверяет является ли объект группой элементов.
//
// Параметры:
//  Объект       - объект, Ссылка, ДанныеФормыСтруктура по типу Объект.
//
// Возвращаемое значение:
//  Булево
//
Функция ОбъектЯвляетсяГруппой(Объект) Экспорт
	
	Если ЗначениеСсылочногоТипа(Объект) Тогда
		Ссылка = Объект;
	Иначе
		Ссылка = Объект.Ссылка;
	КонецЕсли;
	
	МетаданныеОбъекта = Ссылка.Метаданные();
	
	Если Метаданные.Справочники.Содержит(МетаданныеОбъекта) Тогда
		
		Если НЕ МетаданныеОбъекта.Иерархический
		 ИЛИ МетаданныеОбъекта.ВидИерархии
		     <> Метаданные.СвойстваОбъектов.ВидИерархии.ИерархияГруппИЭлементов Тогда
			
			Возврат Ложь;
		КонецЕсли;
		
	ИначеЕсли НЕ Метаданные.ПланыВидовХарактеристик.Содержит(МетаданныеОбъекта) Тогда
		Возврат Ложь;
		
	ИначеЕсли НЕ МетаданныеОбъекта.Иерархический Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Ссылка <> Объект Тогда
		Возврат Объект.ЭтоГруппа;
	КонецЕсли;
	
	Возврат ЗначениеРеквизитаОбъекта(Ссылка, "ЭтоГруппа");
	
КонецФункции

// Проверить, что значение имеет ссылочный тип данных.
//
// Параметры:
//  Значение - ЛюбаяСсылка
//
// Возвращаемое значение:
//  Булево
//
Функция ЗначениеСсылочногоТипа(Значение) Экспорт
	
	Если Значение = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Перечисления.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ПланыВидовХарактеристик.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ПланыСчетов.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ПланыВидовРасчета.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если БизнесПроцессы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если БизнесПроцессы.ТипВсеСсылкиТочекМаршрутаБизнесПроцессов().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Задачи.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ПланыОбмена.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Возвращает структуру, содержащую значения реквизитов прочитанные из информационной базы
// по ссылке на объект.
// 
//  Если доступа к одному из реквизитов нет, возникнет исключение прав доступа.
//  Если необходимо зачитать реквизит независимо от прав текущего пользователя,
//  то следует использовать предварительный переход в привилегированный режим.
// 
// Параметры:
//  Ссылка    - ЛюбаяСсылка
//
//  Реквизиты - Строка - имена реквизитов, перечисленные через запятую, в формате
//              требований к свойствам структуры.
//              Например, "Код, Наименование, Родитель".
//            - Структура
//            - ФиксированнаяСтруктура - в качестве ключа передается
//              псевдоним поля для возвращаемой структуры с результатом, а в качестве
//              значения (опционально) фактическое имя поля в таблице.
//              Если значение не определено, то имя поля берется из ключа.
//            - Массив
//            - ФиксированныйМассив - имена реквизитов в формате требований
//              к свойствам структуры.
//
// Возвращаемое значение:
//  Структура - содержит имена (ключи) и значения затребованных реквизитов.
//              Если строка затребованных реквизитов пуста, то возвращается пустая структура.
//
Функция ЗначенияРеквизитовОбъекта(Ссылка, Знач Реквизиты) Экспорт
	
	Если ТипЗнч(Реквизиты) = Тип("Строка") Тогда
		Если ПустаяСтрока(Реквизиты) Тогда
			Возврат Новый Структура;
		КонецЕсли;
		Реквизиты = СтрРазделить(Реквизиты, ",", Ложь);
	КонецЕсли;
	
	СтруктураРеквизитов = Новый Структура;
	Если ТипЗнч(Реквизиты) = Тип("Структура") Или ТипЗнч(Реквизиты) = Тип("ФиксированнаяСтруктура") Тогда
		СтруктураРеквизитов = Реквизиты;
	ИначеЕсли ТипЗнч(Реквизиты) = Тип("Массив") Или ТипЗнч(Реквизиты) = Тип("ФиксированныйМассив") Тогда
		Для Каждого Реквизит Из Реквизиты Цикл
			СтруктураРеквизитов.Вставить(СтрЗаменить(Реквизит, ".", ""), Реквизит);
		КонецЦикла;
	Иначе
		ВызватьИсключение ПодставитьПараметрыВСтроку(НСтр("ru = 'Неверный тип второго параметра Реквизиты: %1'"), Строка(ТипЗнч(Реквизиты)));
	КонецЕсли;
	
	ТекстПолей = "";
	Для Каждого КлючИЗначение Из СтруктураРеквизитов Цикл
		ИмяПоля   = ?(ЗначениеЗаполнено(КлючИЗначение.Значение),
		              СокрЛП(КлючИЗначение.Значение),
		              СокрЛП(КлючИЗначение.Ключ));
		
		Псевдоним = СокрЛП(КлючИЗначение.Ключ);
		
		ТекстПолей  = ТекстПолей + ?(ПустаяСтрока(ТекстПолей), "", ",") + "
		|	" + ИмяПоля + " КАК " + Псевдоним;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Ссылка", Ссылка);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	&ТекстПолей
	|ИЗ
	|	&ИмяТаблицы КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	ПсевдонимЗаданнойТаблицы.Ссылка = &Ссылка
	|";
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ТекстПолей", ТекстПолей);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ИмяТаблицы", Ссылка.Метаданные().ПолноеИмя());
	Выборка = Запрос.Выполнить().Выбрать();
	Выборка.Следующий();
	
	Результат = Новый Структура;
	Для Каждого КлючИЗначение Из СтруктураРеквизитов Цикл
		Результат.Вставить(КлючИЗначение.Ключ);
	КонецЦикла;
	ЗаполнитьЗначенияСвойств(Результат, Выборка);
	
	Возврат Результат;
	
КонецФункции

// Возвращает значение реквизита, прочитанного из информационной базы по ссылке на объект.
//
//  Если доступа к реквизиту нет, возникнет исключение прав доступа.
//  Если необходимо зачитать реквизит независимо от прав текущего пользователя,
//  то следует использовать предварительный переход в привилегированный режим.
//
// Параметры:
//  Ссылка    - ЛюбаяСсылка
//  ИмяРеквизита - Строка - например, "Код".
//
// Возвращаемое значение:
//  Произвольный    - зависит от типа значения прочитанного реквизита.
//
Функция ЗначениеРеквизитаОбъекта(Ссылка, ИмяРеквизита) Экспорт
	
	Результат = ЗначенияРеквизитовОбъекта(Ссылка, ИмяРеквизита);
	Возврат Результат[СтрЗаменить(ИмяРеквизита, ".", "")];
	
КонецФункции 

// Возвращает Истина, если подсистема существует.
//
// Параметры:
//  ПолноеИмяПодсистемы - Строка - полное имя объекта метаданных подсистема без слов "Подсистема.".
//                        Например: "СтандартныеПодсистемы.БазоваяФункциональность".
//
// Пример вызова необязательной подсистемы:
//
//  Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
//  	МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
//  	МодульУправлениеДоступом.<Имя метода>();
//  КонецЕсли;
//
// Возвращаемое значение:
//  Булево
//
Функция ПодсистемаСуществует(ПолноеИмяПодсистемы) Экспорт
	
	ИменаПодсистем = ИменаПодсистем();
	Возврат ИменаПодсистем.Получить(ПолноеИмяПодсистемы) <> Неопределено;
	
КонецФункции

// Возвращает соответствие имен подсистем и значения Истина;
Функция ИменаПодсистем() Экспорт
	
	Возврат Новый ФиксированноеСоответствие(ИменаПодчиненныхПодсистем(Метаданные));
	
КонецФункции

Функция ИменаПодчиненныхПодсистем(РодительскаяПодсистема)
	
	Имена = Новый Соответствие;
	
	Для Каждого ТекущаяПодсистема Из РодительскаяПодсистема.Подсистемы Цикл
		
		Имена.Вставить(ТекущаяПодсистема.Имя, Истина);
		ИменаПодчиненных = ИменаПодчиненныхПодсистем(ТекущаяПодсистема);
		
		Для каждого ИмяПодчиненной Из ИменаПодчиненных Цикл
			Имена.Вставить(ТекущаяПодсистема.Имя + "." + ИмяПодчиненной.Ключ, Истина);
		КонецЦикла;
	КонецЦикла;
	
	Возврат Имена;
	
КонецФункции

// Возвращает ссылку на общий модуль по имени.
//
// Параметры:
//  Имя          - Строка - имя общего модуля, например:
//                 "ОбщегоНазначения",
//                 "ОбщегоНазначенияКлиент".
//
// Возвращаемое значение:
//  ОбщийМодуль
//
Функция ОбщийМодуль(Имя) Экспорт
	
	Если Метаданные.ОбщиеМодули.Найти(Имя) <> Неопределено Тогда
		Модуль = Вычислить(Имя); // АПК:488 "Вычислить" вместо "ОбщегоНазначения.ВычислитьВБезопасномРежиме()", так как это автономная обработка.
	Иначе
		Модуль = Неопределено;
	КонецЕсли;
	
	Если ТипЗнч(Модуль) <> Тип("ОбщийМодуль") Тогда
		ВызватьИсключение ПодставитьПараметрыВСтроку(НСтр("ru = 'Общий модуль ""%1"" не существует.'"), Имя);
	КонецЕсли;
	
	Возврат Модуль;
	
КонецФункции

Функция ПодставитьПараметрыВСтроку(Знач СтрокаПодстановки,
	Знач Параметр1, Знач Параметр2 = Неопределено, Знач Параметр3 = Неопределено)
	
	СтрокаПодстановки = СтрЗаменить(СтрокаПодстановки, "%1", Параметр1);
	СтрокаПодстановки = СтрЗаменить(СтрокаПодстановки, "%2", Параметр2);
	СтрокаПодстановки = СтрЗаменить(СтрокаПодстановки, "%3", Параметр3);
	
	Возврат СтрокаПодстановки;
КонецФункции

// АПК:487-выкл Копия ВыполнитьВБезопасномРежиме() и ВычислитьВБезопасномРежиме() модуля ОбщегоНазначения т.к. обработка
// "отторгаемая".

// Выполняет произвольный алгоритм на встроенном языке 1С:Предприятия, предварительно устанавливая
//  безопасный режим выполнения кода и безопасный режим разделения данных для всех разделителей,
//  присутствующих в составе конфигурации. В результате при выполнении алгоритма:
//   - игнорируются попытки установки привилегированного режима,
//   - запрещаются все внешние (по отношению к платформе 1С:Предприятие) действия (COM,
//       загрузка внешних компонент, запуск внешних приложений и команд операционной системы,
//       доступ к файловой системе и Интернет-ресурсам),
//   - запрещается отключение использования разделителей сеанса,
//   - запрещается изменение значений разделителей сеанса (если разделение данным разделителем не
//       является условно выключенным),
//   - запрещается изменение объектов, которые управляют состоянием условного разделения.
//
// Параметры:
//  Алгоритм - Строка - содержащая произвольный алгоритм на встроенном языке 1С:Предприятия.
//  Параметры - Произвольный - в качестве значения данного параметра может быть передано значение,
//    которое требуется для выполнения алгоритма (при этом в тексте алгоритма обращение к данному
//    значению должно осуществляться как к имени переменной Параметры).
//
Процедура ВыполнитьВБезопасномРежиме(Знач Алгоритм, Знач Параметры = Неопределено) Экспорт
	
	УстановитьБезопасныйРежим(Истина);
	
	МассивРазделителей = РазделителиКонфигурации();
	
	Для Каждого ИмяРазделителя Из МассивРазделителей Цикл
		
		УстановитьБезопасныйРежимРазделенияДанных(ИмяРазделителя, Истина);
		
	КонецЦикла;
	
	Выполнить Алгоритм;
	
КонецПроцедуры

// Вычисляет переданное выражение, предварительно устанавливая безопасный режим выполнения кода
// и безопасный режим разделения данных для всех разделителей, присутствующих в составе конфигурации.
//
// Параметры:
//  Выражение - Строка - выражение на встроенном языке 1С:Предприятия.
//  Параметры - Произвольный - контекст, который требуется для вычисления выражения.
//    В тексте выражения обращение к контексту должно происходить по имени "Параметры".
//    Например, выражение "Параметры.Значение1 = Параметры.Значение2" обращается к значениям
//    "Значение1" и "Значение2" переданные в Параметры, как свойства.
//
// Возвращаемое значение:
//   Произвольный - результат вычисления выражения.
//
// Пример:
//
//  // Пример 1
//  Параметры = Новый Структура;
//  Параметры.Вставить("Значение1", 1);
//  Параметры.Вставить("Значение2", 10);
//  Результат = ОбщегоНазначения.ВычислитьВБезопасномРежиме("Параметры.Значение1 = Параметры.Значение2", Параметры);
//
//  // Пример 2
//  Результат = ОбщегоНазначения.ВычислитьВБезопасномРежиме("СтандартныеПодсистемыСервер.ВерсияБиблиотеки()");
//
Функция ВычислитьВБезопасномРежиме(Знач Выражение, Знач Параметры = Неопределено) Экспорт
	
	УстановитьБезопасныйРежим(Истина);
	
	Если ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		МодульРаботаВМоделиСервиса = ОбщийМодуль("РаботаВМоделиСервиса");
		МассивРазделителей = МодульРаботаВМоделиСервиса.РазделителиКонфигурации();
	Иначе
		МассивРазделителей = Новый Массив;
	КонецЕсли;
	
	Для Каждого ИмяРазделителя Из МассивРазделителей Цикл
		
		УстановитьБезопасныйРежимРазделенияДанных(ИмяРазделителя, Истина);
		
	КонецЦикла;
	
	Возврат Вычислить(Выражение);
	
КонецФункции

// АПК:487-вкл

// Возвращает массив существующих в конфигурации разделителей.
//
// Возвращаемое значение:
//   ФиксированныйМассив из Строка - массив имен общих реквизитов, которые
//  являются разделителями.
//
Функция РазделителиКонфигурации() Экспорт
	
	МассивРазделителей = Новый Массив;
	
	Для Каждого ОбщийРеквизит Из Метаданные.ОбщиеРеквизиты Цикл
		Если ОбщийРеквизит.РазделениеДанных = Метаданные.СвойстваОбъектов.РазделениеДанныхОбщегоРеквизита.Разделять Тогда
			МассивРазделителей.Добавить(ОбщийРеквизит.Имя);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Новый ФиксированныйМассив(МассивРазделителей);
	
КонецФункции

#Область ИзменениеОбъектовВНесколькоПотоков

// АПК:581-выкл Экспортная, так как вызывается из фонового задания.
Функция РезультатИзмененияПорцииОбъектов(ОбрабатываемыеОбъекты, РезультатИзменения, НастройкиИзменения) Экспорт

	Ссылка         = Неопределено;
	ЗаписатьОшибку = Истина;
	
	ОтключитьОбновлениеКлючейДоступа(Истина);
	Если НастройкиИзменения.ИзменятьВТранзакции Тогда
		НачатьТранзакцию();
	КонецЕсли;
	
	Попытка
		Если НастройкиИзменения.ИзменятьВТранзакции Тогда
			Блокировка = Новый БлокировкаДанных;
			Для Каждого ДанныеОбъекта Из ОбрабатываемыеОбъекты Цикл
				Ссылка = ДанныеОбъекта.Ссылка;
				ЗаблокироватьСсылку(Блокировка, ДанныеОбъекта.Ссылка);
			КонецЦикла;
			Блокировка.Заблокировать();
		КонецЕсли;
		
		Для Каждого ДанныеОбъекта Из ОбрабатываемыеОбъекты Цикл
			
			ЗаписатьОшибку = Истина;
			НачатьТранзакцию();
			Попытка
				
				Ссылка = ДанныеОбъекта.Ссылка;
				Если Не НастройкиИзменения.ИзменятьВТранзакции Тогда
					Блокировка = Новый БлокировкаДанных;
					ЗаблокироватьСсылку(Блокировка, Ссылка);
					Блокировка.Заблокировать();
				КонецЕсли;
				
				ИзменяемыйОбъект = Ссылка.ПолучитьОбъект();
				
				Изменения = Неопределено;
				Если НастройкиИзменения.ТипОперации = "ВыполнитьАлгоритм" Тогда
					ВыполнитьКодАлгоритма(ИзменяемыйОбъект, НастройкиИзменения.КодАлгоритма,
						НастройкиИзменения.ВыполнятьКодАлгоритмаВБезопасномРежиме);
				Иначе
					Изменения = ВнестиИзменения(ДанныеОбъекта, ИзменяемыйОбъект, НастройкиИзменения);
				КонецЕсли;
				
				// Режим записи.
				ЭтоДокумент = Метаданные.Документы.Содержит(ИзменяемыйОбъект.Метаданные());
				РежимЗаписи = ОпределитьРежимЗаписи(ИзменяемыйОбъект, ЭтоДокумент, НастройкиИзменения.РежимРазработчика);
				
				// Проверка заполнения.
				Если Не НастройкиИзменения.РежимРазработчика Тогда
					Если Не ЭтоДокумент Или РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
						Если Не ИзменяемыйОбъект.ПроверитьЗаполнение() Тогда
							ВызватьИсключение ТекстОшибокПроверкиЗаполнения();
						КонецЕсли;
					КонецЕсли;
				КонецЕсли;
				
				// Запись дополнительных сведений.
				Если Изменения <> Неопределено И Изменения.МассивЗаписейДопСведений.Количество() > 0 Тогда
					Для Каждого МенеджерЗаписи Из Изменения.МассивЗаписейДопСведений Цикл
						МенеджерЗаписи.Записать(Истина);
					КонецЦикла;
				КонецЕсли;
				
				НастроеныИзменения = ЗначениеЗаполнено(НастройкиИзменения.ИзменяемыеРеквизиты)
					Или ЗначениеЗаполнено(НастройкиИзменения.ИзменяемыеТабличныеЧасти);
				
				ЗаписыватьОбъект = НастройкиИзменения.ВариантЗаписиОбъекта <> "НеЗаписывать"
					И (ИзменяемыйОбъект.Модифицированность() Или Не НастроеныИзменения);
				
				// Запись объекта.
				Если ЗаписыватьОбъект Тогда
					Если РежимЗаписи <> Неопределено Тогда
						ИзменяемыйОбъект.Записать(РежимЗаписи);
					Иначе
						ИзменяемыйОбъект.Записать();
					КонецЕсли;
				КонецЕсли;
				
				ЗаполнитьРезультатИзмененияДополнительныхСвойств(РезультатИзменения, Ссылка, ИзменяемыйОбъект, Изменения);
				
				РазблокироватьДанныеДляРедактирования(Ссылка);
				ЗафиксироватьТранзакцию();
				
			Исключение
				
				ОтменитьТранзакцию();
				Если НастройкиИзменения.ИзменятьВТранзакции Тогда
					РазблокироватьДанныеДляРедактирования(Ссылка);
				КонецЕсли;
				
				КраткоеПредставлениеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
				ЗаполнитьРезультатИзменения(РезультатИзменения, Ссылка, КраткоеПредставлениеОшибки);
				Если НастройкиИзменения.ОстанавливатьИзменениеПриОшибке Или НастройкиИзменения.ИзменятьВТранзакции Тогда
					ЗаписатьОшибку = Ложь;
					ВызватьИсключение;
				КонецЕсли;
				
				Продолжить;
			КонецПопытки;
			
		КонецЦикла;
		
		ОтключитьОбновлениеКлючейДоступа(Ложь);
		Если НастройкиИзменения.ИзменятьВТранзакции Тогда
			ЗафиксироватьТранзакцию();
		КонецЕсли;
		
	Исключение
		
		Если НастройкиИзменения.ИзменятьВТранзакции Тогда 
			ОтменитьТранзакцию();
			Для Каждого ДанныеОбъекта Из ОбрабатываемыеОбъекты Цикл
				РазблокироватьДанныеДляРедактирования(ДанныеОбъекта.Ссылка);
			КонецЦикла;
		КонецЕсли;
		
		ОтключитьОбновлениеКлючейДоступа(Ложь, НастройкиИзменения.ИзменятьВТранзакции);
		
		Если ЗаписатьОшибку Тогда
			КраткоеПредставлениеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаполнитьРезультатИзменения(РезультатИзменения, Ссылка, КраткоеПредставлениеОшибки);
		КонецЕсли;
		
	КонецПопытки;
	
	Возврат РезультатИзменения;
	
КонецФункции
// АПК:581-вкл

Функция ВыполнитьИзменениеОбъектовВНесколькоПотоков(Параметры, ОбрабатываемыеОбъекты, РезультатИзменения,
		ОстанавливатьИзменениеПриОшибке, ВыполнятьКодАлгоритмаВБезопасномРежиме)
	
	ИдентификаторФормы = Новый УникальныйИдентификатор;
	
	МодульДлительныеОперации = ОбщийМодуль("ДлительныеОперации");
	
	ПараметрыВыполнения = МодульДлительныеОперации.ПараметрыВыполненияВФоне(ИдентификаторФормы);
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Групповое изменение реквизитов'");
	
	ЭтоВнешняяОбработка = ЭтоВнешняяОбработка();
	Если ЭтоВнешняяОбработка Тогда
		ПараметрыВыполнения.ВнешнийОтчетОбработка = Параметры.ВнешнийОтчетОбработка;
	КонецЕсли;
	
	МассивПорций    = Новый Массив;
	ПорцияОбъектов  = Новый Массив;
	СчетчикОбъектов = 0;
	
	КоличествоПотоковДлительныхОпераций = МодульДлительныеОперации.ДопустимоеКоличествоПотоков();
	
	ОбрабатыватьВОдинПоток = Параметры.ИзменятьВТранзакции Или Параметры.ПрерыватьПриОшибке; 
	Если КоличествоПотоковДлительныхОпераций > 0 И Не ОбрабатыватьВОдинПоток Тогда
		ЧислоОбъектовВПорции = Цел(ОбрабатываемыеОбъекты.Количество() / КоличествоПотоковДлительныхОпераций);
	Иначе
		ЧислоОбъектовВПорции = ОбрабатываемыеОбъекты.Количество();
	КонецЕсли;
	
	ОписаниеТабличныхЧастей = ОписаниеТабличныхЧастей(Параметры);
	
	Для Каждого ДанныеОбъекта Из ОбрабатываемыеОбъекты Цикл
		Если СчетчикОбъектов = ЧислоОбъектовВПорции Тогда
			МассивПорций.Добавить(ПорцияОбъектов);
			ПорцияОбъектов  = Новый Массив;
			СчетчикОбъектов = 0;
		КонецЕсли;
		
		ТабличныеЧасти = ОписаниеТабличныхЧастей.Скопировать();
		
		РекурсивноеДобавлениеСтрокДереваЗначений(ТабличныеЧасти, ДанныеОбъекта);
		
		ДанныеПорции = Новый Структура;
		ДанныеПорции.Вставить("Ссылка",         ДанныеОбъекта.Ссылка);
		ДанныеПорции.Вставить("ТабличныеЧасти", ТабличныеЧасти);
		
		ПорцияОбъектов.Добавить(ДанныеПорции);
		
		СчетчикОбъектов = СчетчикОбъектов + 1;
	КонецЦикла;
	
	Если ПорцияОбъектов.Количество() > 0 Тогда
		МассивПорций.Добавить(ПорцияОбъектов);
	КонецЕсли;
	
	ПараметрыМетода      = Новый Соответствие;
	ВерхняяГраницаПорций = МассивПорций.ВГраница();
	
	НастройкиИзменения = НастройкиИзменения(Параметры, ВыполнятьКодАлгоритмаВБезопасномРежиме);
	
	Для ИндексПорции = 0 По ВерхняяГраницаПорций Цикл
		МассивПараметров = Новый Массив;
		МассивПараметров.Добавить(МассивПорций[ИндексПорции]);
		МассивПараметров.Добавить(РезультатИзменения);
		МассивПараметров.Добавить(НастройкиИзменения);
		
		ПараметрыМетода.Вставить(ИндексПорции, МассивПараметров);
	КонецЦикла;
	
	ИмяФункции = СтрШаблон("%1.ГрупповоеИзменениеРеквизитов.МодульОбъекта.РезультатИзмененияПорцииОбъектов",
		?(ЭтоВнешняяОбработка, "ВнешняяОбработка", "Обработка"));
	
	РезультатВыполнения = МодульДлительныеОперации.ВыполнитьФункциюВНесколькоПотоков(ИмяФункции,
		ПараметрыВыполнения, ПараметрыМетода);
		
	Возврат РезультатВыполнения;
	
КонецФункции

Функция ОписаниеТабличныхЧастей(Параметры)

	ОписаниеТабличныхЧастей = Новый ДеревоЗначений;
	
	ОбъектыДляИзменения = Параметры.ОбъектыДляИзменения.Получить();
	Если ОбъектыДляИзменения = Неопределено Тогда
		ОбъектыДляИзменения = Параметры.ОбрабатываемыеОбъекты.Получить();
	КонецЕсли;
	
	Для Каждого Колонка Из ОбъектыДляИзменения.Колонки Цикл
		ОписаниеТабличныхЧастей.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения, Колонка.Заголовок, Колонка.Ширина);
	КонецЦикла;
	
	Возврат ОписаниеТабличныхЧастей;
	
КонецФункции

Процедура РекурсивноеДобавлениеСтрокДереваЗначений(Получатель, Источник)
	
	Для Каждого СтрокаИсточника Из Источник.Строки Цикл
		
		ТекущаяСтрока = Получатель.Строки.Добавить();
		ЗаполнитьЗначенияСвойств(ТекущаяСтрока, СтрокаИсточника);
		
		Если СтрокаИсточника.Строки.Количество() > 0 Тогда
			РекурсивноеДобавлениеСтрокДереваЗначений(ТекущаяСтрока, СтрокаИсточника);
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// Для внутреннего использования.
Функция ЭтоВнешняяОбработка()
	
	СтруктураОбъекта = Новый Структура;
	СтруктураОбъекта.Вставить("ИспользуемоеИмяФайла", Неопределено);
	ЗаполнитьЗначенияСвойств(СтруктураОбъекта, ЭтотОбъект);
	
	Возврат (СтруктураОбъекта.ИспользуемоеИмяФайла <> Неопределено);
	
КонецФункции

Функция НастройкиИзменения(Параметры, ВыполнятьКодАлгоритмаВБезопасномРежиме)
	
	НастройкиИзменения = Новый Структура;
	НастройкиИзменения.Вставить("ВариантЗаписиОбъекта");
	НастройкиИзменения.Вставить("ВыполнятьКодАлгоритмаВБезопасномРежиме", ВыполнятьКодАлгоритмаВБезопасномРежиме);
	НастройкиИзменения.Вставить("ДоступныеРеквизиты");
	НастройкиИзменения.Вставить("ИзменяемыеРеквизиты");
	НастройкиИзменения.Вставить("ИзменяемыеТабличныеЧасти");
	НастройкиИзменения.Вставить("ИзменятьВТранзакции");
	НастройкиИзменения.Вставить("КодАлгоритма");
	НастройкиИзменения.Вставить("ОстанавливатьИзменениеПриОшибке");
	НастройкиИзменения.Вставить("РежимРазработчика");
	НастройкиИзменения.Вставить("ТипОперации");
	
	ЗаполнитьЗначенияСвойств(НастройкиИзменения, Параметры);
	
	Возврат НастройкиИзменения;
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Иначе
ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли